﻿using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System.Text;

namespace CSharpAllDemo.Tcp
{

    public class DotnettyManager
    {
        public delegate void WaitReceivedCallback(bool isSuccess, byte[] messageByte);
        public delegate void ReceivedCallback(byte[] messageByte);

        // 私有静态实例
        private static readonly DotnettyManager _instance = new DotnettyManager();
        // 公共静态方法，返回实例
        public static DotnettyManager Instance => _instance;

        private Dictionary<string, IChannelHandlerContext> mContextDictory = new();
        public Dictionary<string, WaitReceivedCallback> mWaitReceivedCallbackDictory = new();
        public ReceivedCallback? mReceivedCallback = null;



        private DotnettyManager() { }
        public async Task InitAsync(int port)
        {

            // 主工作线程组，设置为1个线程
            var bossGroup = new MultithreadEventLoopGroup(1);
            // 工作线程组，默认为内核数*2的线程数
            var workerGroup = new MultithreadEventLoopGroup();//声明一个服务端Bootstrap，每个Netty服务端程序，都由ServerBootstrap控制，
                                                              //通过链式的方式组装需要的参数
            var bootstrap = new ServerBootstrap();
            bootstrap
            .Group(bossGroup, workerGroup) // 设置主和工作线程组
            .Channel<TcpServerSocketChannel>() // 设置通道模式为TcpSocket
            .Option(ChannelOption.SoBacklog, 100) // 设置网络IO参数等，这里可以设置很多参数，当然你对网络调优和参数设置非常了解的话，你可以设置，或者就用默认参数吧
            .Option(ChannelOption.SoKeepalive, true)//保持连接
                                                    //.Option(ChannelOption.RcvbufAllocator, new FixedRecvByteBufAllocator(4000))
           .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
           {
               //工作线程连接器 是设置了一个管道，服务端主线程所有接收到的信息都会通过这个管道一层层往下传输
               //同时所有出栈的消息 也要这个管道的所有处理器进行一步步处理
               IChannelPipeline pipeline = channel.Pipeline;
               pipeline.AddLast(new CommonServerDecoder());
               pipeline.AddLast(new CommonServerEncoder());
               pipeline.AddLast(new IdleStateHandler(5, 0, 0));
               //业务handler ，这里是实际处理业务的Handler
               pipeline.AddLast(new TcpServerHandler(this));
           }));

            // bootstrap绑定到指定端口的行为 就是服务端启动服务，同样的Serverbootstrap可以bind到多个端口
            IChannel boundChannel = await bootstrap.BindAsync(port);
        }
        public void Write(string deviceId, byte[] message)
        {
            mContextDictory[deviceId]?.WriteAndFlushAsync(message);
        }

        public void WriteCallBack(string deviceId, byte[] message, WaitReceivedCallback waitReceivedCallback, int milliseconds)
        {
            mContextDictory[deviceId]?.WriteAndFlushAsync(message);
            this.mWaitReceivedCallbackDictory[deviceId] = waitReceivedCallback;
            Task.Factory.StartNew(async () =>
            {
                await Task.Delay(milliseconds);
                if (this.mWaitReceivedCallbackDictory[deviceId] != null)
                {
                    this.mWaitReceivedCallbackDictory[deviceId](false, null);
                    this.mWaitReceivedCallbackDictory.Remove(deviceId);
                }

            });

        }



    }
    class TcpServerHandler : ChannelHandlerAdapter
    {
        private DotnettyManager mDotnettyManager;
        public TcpServerHandler(DotnettyManager dotnettyManager)
        {
            mDotnettyManager = dotnettyManager;
        }
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            //if (message is byte[] messageBytes)
            //{
            //    string srt = Encoding.UTF8.GetString(o);
            //    context.WriteAndFlushAsync(srt);
            //    //System.Diagnostics.Debug.WriteLine($"byte方式，从服务端接收:{Encoding.UTF8.GetString(o)}");

            //}
            if (message is byte[] messageBytes)
            {
                //收到数据
                if (mDotnettyManager.mWaitReceivedCallbackDictory["deviceId"] != null)
                {
                    mDotnettyManager.mWaitReceivedCallbackDictory["deviceId"](true, messageBytes);
                    mDotnettyManager.mWaitReceivedCallbackDictory.Remove("deviceId");
                }
                if (mDotnettyManager.mReceivedCallback != null)
                {
                    mDotnettyManager.mReceivedCallback(messageBytes);
                }
            }

        }
        public override void UserEventTriggered(IChannelHandlerContext context, object evt)
        {
            if (evt is IdleStateEvent)
            {
                IdleStateEvent e = evt as IdleStateEvent;
                if (e.State == IdleState.AllIdle)
                {
                    //读或者写
                    System.Diagnostics.Debug.WriteLine("==AllIdle==");
                }
                else if (e.State == IdleState.ReaderIdle)
                {
                    //读
                    System.Diagnostics.Debug.WriteLine("==ReaderIdle==");

                }
                else if (e.State == IdleState.WriterIdle)
                {
                    System.Diagnostics.Debug.WriteLine("==WriterIdle==");
                }
            }

        }
    }


    class CommonServerDecoder : ByteToMessageDecoder
    {
        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
        {
            byte[] array = new byte[input.ReadableBytes];
            input.GetBytes(input.ReaderIndex, array, 0, input.ReadableBytes);
            input.Clear();
            output.Add(array);
        }
    }

    class CommonServerEncoder : MessageToByteEncoder<string>
    {
        protected override void Encode(IChannelHandlerContext context, string message, IByteBuffer output)
        {
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);
            IByteBuffer initialMessage = Unpooled.Buffer(messageBytes.Length);
            initialMessage.WriteBytes(messageBytes);
            output.WriteBytes(initialMessage);
        }
    }



}
